package com.shop.shopserver.generator;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.shop.shopcommon.constant.MessageConstant;
import com.shop.shopcommon.context.SecurityContext;
import com.shop.shopcommon.exception.Base.UserNotLoginException;
import com.shop.shoppojo.entity.User;
import com.shop.shopserver.annotation.RequestKeyParam;
import com.shop.shopserver.annotation.RequestLock;
import com.shop.shopserver.mapper.UserMapper;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
@Component
@Slf4j
//接口锁生成器
public class RequestKeyGenerator {
//    @Autowired
//    private static UserMapper userMapper;
//    public static String getUserId() {
//        String username = SecurityContext.getUsername();
//        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
//        queryWrapper.eq(User::getUsername, username);
//        User user = userMapper.selectOne(queryWrapper);
//        return user.getId().toString();
//    }

    public static String getLockKey(ProceedingJoinPoint joinPoint) {
        // 使用 Spring AOP（面向切面编程）时用于获取被拦截方法的相关信息，比如方法签名、参数
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        // 获取方法上的注解以及参数等信息
        Method method = methodSignature.getMethod();
        // 从获取到的Method对象上获取RequestLock注解
        RequestLock requestLock = method.getAnnotation(RequestLock.class);
        // 获取方法参数 获取被拦截方法的实际参数值数组args
        final Object[] args = joinPoint.getArgs();
        // 通过method获取方法的参数列表parameters
        final Parameter[] parameters = method.getParameters();
        // 遍历方法的参数列表，对于每个参数，检查其是否有RequestKeyParam注解。
        // 如果有该注解，就将RequestLock注解中定义的分隔符（通过requestLock.delimiter()获取）和该参数的值（args[i]）添加到StringBuilder中。
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < parameters.length; i++) {
            final RequestKeyParam keyParam = parameters[i].getAnnotation(RequestKeyParam.class);
            if (keyParam == null) {
                continue;
            }
            sb.append(requestLock.delimiter()).append(args[i]);
        }

        // 前者处理的是方法参数，后者处理的是方法参数对象中的字段。
        // 首先获取方法参数的所有注解二维数组parameterAnnotations，然后遍历每个参数。对于每个参数对象，获取其所有声明的字段fields，
        // 再遍历这些字段，检查是否有RequestKeyParam注解。
        // 如果有，就通过反射（先设置字段可访问field.setAccessible(true)，
        // 再使用ReflectionUtils.getField(field, object)获取字段值）获取字段的值，并将RequestLock注解中的分隔符和字段值添加到StringBuilder
        if (StringUtils.isEmpty(sb.toString())) {
            final Annotation[][] parameterAnnotations = method.getParameterAnnotations();
            for (int i = 0; i < parameterAnnotations.length; i++) {
                final Object object = args[i];
                final Field[] fields = object.getClass().getDeclaredFields();
                for (Field field : fields) {
                    final RequestKeyParam annotation = field.getAnnotation(RequestKeyParam.class);
                    if (annotation == null) {
                        continue;
                    }
                    field.setAccessible(true);
                    sb.append(requestLock.delimiter()).append(ReflectionUtils.getField(field, object));
                }
            }
        }
        // 然后将RequestLock注解中定义的键前缀（通过requestLock.prefix()获取）与前面构建好的锁键部分（sb）拼接起来并作为方法的返回值返回
        //
        Long userId = SecurityContext.getUserId();
        if (userId==null){
            // 抛出用户为登陆异常
            throw new UserNotLoginException(MessageConstant.USER_NOT_LOGIN);
        }
        log.info("锁为："+requestLock.prefix() + "&" + userId + sb);
        return requestLock.prefix() + "&" + userId + sb;
    }
}
